#ifndef __MEMORY_BUFFER__
#define __MEMORY_BUFFER__

#include<sys/ioctl.h>
#include<unistd.h>

#include"chunk.hpp"
#include"memorypool.hpp"
#include"../log/log.hpp"

class Buffer
{
public:
    Buffer() = default;

    const int length()const{return _buff_data?_buff_data->_length:0;}

    void pop(int len)
    {
        assert(_buff_data&&len<=_buff_data->_length);

        _buff_data->pop(len);
        if(_buff_data->_length==0)
        {
            Memorypool::get_instance().push(_buff_data);
            _buff_data=nullptr;
        }
    }

    void clear()
    {
        if(_buff_data)
        {
            Memorypool::get_instance().push(_buff_data);
            _buff_data=nullptr;
        }
    }

    ~Buffer()
    {
        clear();
    }

protected:
    Chunkptr _buff_data{nullptr};
};

//读取缓冲区
class InBuffer:public Buffer
{
public:
    //从对应文件流中读取数据到buff
    int read_from_fd(int fd)
    {
        int need_read;
        //ioctl函数设备驱动程序中对设备的I/O通道进行管理的函数
        //FIONREAD获取接收缓存区中的字节数
        if(ioctl(fd,FIONREAD,&need_read)==-1)
        {
            ERR_LOG("ioctl FIONREAD error\n");
            return -1;
        }

        //判断buff是否存在，若不存在，则向内存池申请，若存在，则判断buff空间是否足够存放将要读取内容
        //若不够，则向内存池申请一块足够的内存块，替换现在的buff
        if(_buff_data==nullptr)
        {
            _buff_data=Memorypool::get_instance().pop(need_read);
            if(_buff_data==nullptr)
            {
                INF_LOG("no free buff for pop");
                return -1;
            }
        }
        else
        {
            assert(_buff_data->_head==0);
            if(_buff_data->_capacity-_buff_data->_length<need_read)
            {
                Chunkptr newbuff=Memorypool::get_instance().pop(need_read+_buff_data->_length);
                if (newbuff == nullptr)
                {
                    INF_LOG("no free buff for pop");
                    return -1;
                }
                newbuff->copy(_buff_data);
                Memorypool::get_instance().push(_buff_data);
                _buff_data=newbuff;
            }
        }

        //向文件流中读取内容
        //EINTR表示中断的系统调用
        int already_read=0;
        do
        {
            if(need_read==0)already_read=read(fd,_buff_data->_data+_buff_data->_head,m4K);
            else already_read=read(fd,_buff_data->_data+_buff_data->_head,need_read);
        }while(already_read==-1&&errno==EINTR);

        if(already_read>0)
        {
            if(need_read)assert(already_read==need_read);
            _buff_data->_length+=already_read;
        }

        return already_read;
    }

    //获得buff中内容
    const char* get_from_buff()const
    {
        return _buff_data?_buff_data->_data+_buff_data->_head:nullptr;
    }

    void adjust(){if(_buff_data)_buff_data->adjust();}

};

//写入缓存区
class OutBuffer:public Buffer
{
public:
    //向huff中写入
    int write_to_buff(const char* data,int len)
    {
        if(_buff_data==nullptr)
        {
            _buff_data=Memorypool::get_instance().pop(len);
            if(_buff_data==nullptr)
            {
                INF_LOG("no free buff for pop");
                return -1;
            }
        }
        else
        {
            assert(_buff_data->_head==0);
            if(_buff_data->_capacity-_buff_data->_length<len)
            {
                Chunkptr newbuff=Memorypool::get_instance().pop(len+_buff_data->_length);
                if (newbuff == nullptr)
                {
                    INF_LOG("no free buff for pop");
                    return -1;
                }
                newbuff->copy(_buff_data);
                Memorypool::get_instance().push(_buff_data);
                _buff_data=newbuff;
            }
        }

        memcpy(_buff_data->_data+_buff_data->_length,data,len);
        _buff_data->_length+=len;

        return 0;
    }

    //向文件流中写入
    int write_to_fd(int fd)
    {
        assert(_buff_data&&_buff_data->_head==0);

        int already_write=0;
        do
        {
            already_write=write(fd,_buff_data->_data,_buff_data->_length);
        }while(already_write==-1&&errno==EINTR);

        if(already_write>0)
        {
            _buff_data->pop(already_write);
            _buff_data->adjust();
        }

        if(already_write==-1&&errno==EINTR)already_write=0;

        return already_write;
    }

};

#endif